#!/bin/bash
# write by zhongcheng@whu.edu.cn 2023.08.30 
# contact me if you have any advice or find any bug
function helpfunc() {
    echo "use Multiwfn/obabel to batch convert one format to another"
    echo "require Multiwfn obabel install in the path"
    echo "usage: mfc -f xyz *.mol"
    echo "       -f set the output format"
    echo "       -e engine of format convert  m for Multiwfn, o for obabel"
    echo "       -i input file format, needed by obabel, default extract from file suffix"
    echo "       -o output file name, default have same basename as input file"
    echo "       -r relax the structure with conformation search using obabel (automatic for 2D input)"
}

if [ $# -lt 1 ]; then
    helpfunc
    exit 1
fi
while getopts "f:e:i:o:r" opt
do
    case $opt in
        f) OUTFMT=$OPTARG;;
        o) OUTFLNM=$OPTARG;; 
        e) ENGINE=$OPTARG;;
        i) INPUTFMT=$OPTARG;;
        r) RELAX=ON;;
        ?) helpfunc
        exit 1;;
    esac
done
shift $(($OPTIND - 1))

# check if Multiwfn is  available 

if [[ -z "$ENGINE" ]];then
    ENGINE=m
fi

MCOM=`command -v Multiwfn`
OCOM=`command -v obabel`
if [[ -z "$MCOM" ]] && [[ $ENGINE==m ]];then
    echo "Multiwfn command not available"
    if [[ -n "$OCOM" ]];then
        echo "try to use obabel instead"
        EIGINE=o
    else
        echo "obabel command not available, exit!"
        exit 1
    fi
elif [[ -z "$OCOM" ]] && [[ $ENGINE==o ]];then
    echo "obabel command not available"
    if [[ -n "$MCOM" ]];then
        echo "try to use Multiwfn instead"
        EIGINE=m
    else
        echo "Multiwfn command not available, exit!"
        exit 1
    fi
fi

function Multiwfn_fc() {
    if [[ -z "$MCOM" ]];then
        echo "Multiwfn command not available"
        echo 1
        return 1
    fi
    not_support_inp="cdx"
    if [[ `strin $INPUTFMT "$not_support_inp"` == 1 ]];then
        echo -n "${1}: Multiwfn do not recognize $INPUTFMT"
        return 1
    fi
    #mout_fmt="xyz pdb cif gro vasp POSCAR gjf"
    #if [[ `strin $OUTFMT "$mout_fmt"` == 1 ]];then
    #    echo "Multiwfn could not output $OUTFMT file"
    #    return 1
    #fi
    declare -A f2c
    f2c["xyz"]=2
    f2c["pdb"]=1
    f2c["cif"]=33
    f2c["gro"]=34
    f2c["vasp"]=27
    f2c["POSCAR"]=27
    f2c["gjf"]=10
    com=`echo ${f2c["$2"]}`
    if [[ $2 == POSCAR ]] || [[ $2 == vasp ]];then
        outname=${1%.*}.POSCAR
    else
        outname=${1%.*}.$2
    fi
    if [ -n "$com" ];then
        echo -e "100\n2\n${com}\n${outname}\n0\nq" | Multiwfn $1 1>/dev/null
        if [ $? -ne 0 ];then
            echo "${1}: something wrong when excuting following command, try is manually:"
            echo "echo -e "100\\n2\\n${com}\\n${outname}\\n0\\nq" | Multiwfn $1"
            return 1
        else
            echo $outname generated by Multiwfn
        fi
    else
        echo -n "${1}: Multiwfn do not recognize $INPUTFMT"
        return 1
    fi 
}

function strin() {
    for word in $2; do
        if [[ "$word" == "$1" ]]; then
            echo 1
            return 1
        fi
    done
    echo 0
    return 0
}

function obabel_fc() {
    if [[ -z "$OCOM" ]];then
        echo "obabel command not available"
        return 1
    fi
    read_format=`obabel  -L formats | grep -v Write-only | cut -d " " -f 1 | tr "\n" " "`
    write_format=`obabel  -L formats | grep -v Read-only | cut -d " " -f 1 | tr "\n" " "`
    relax_format="cdx" 
    if [ -z "$INPUTFMT" ];then
        INPUTFMT="${1##*.}"
    fi
    if [[ `strin $INPUTFMT "$read_format"` == 0 ]];then
        echo "${1}: obabel could not read $INPUTFMT file"
        return 1
    fi
    if [[ `strin $OUTFMT "$write_format"` == 0 ]];then
        echo "${1}: obabel could not output $OUTFMT file"
        return 1
    fi
    if [[ `strin $INPUTFMT "$relax_format"` == 1 ]];then
        RELAX=ON
    fi
    outname=${1%.*}.$2
    if [[ "$RELAX" == "ON" ]];then
        obabel -i $INPUTFMT  $1 -o $OUTFMT -O $outname  --gen3d --conformer --fast --score energy -f UFF
    else
        obabel -i $INPUTFMT  $1 -o $OUTFMT -O $outname 
    fi
    if [ $? -ne 0 ];then
        echo "${1}: something wrong when excuting following command, try it manually:"
        echo "obabel -i $INPUTFMT  $1 -o $OUTFMT -O $outname"
        return 1
    else
        echo $outname generated by obabel
    fi
}

function needhelp () {
    # check if the input format need help 
    # if format in help format, means it will not properly be delt with Multiwfn or openbabel
    # and it will be transformed to fomats that will be recognized
    # this also set INPUTFMT and INP_FILe
    INPUTFMT="${1##*.}"
    INP_FILE=$1
    if [[ $INPUTFMT == "xsd" ]];then
        xsd2xxf $1
        INP_FILE=${1%.*}.gjf
        INPUTFMT=gjf
    fi
    if [[ $INP_FILE == "CONTCAR" ]];then
        cp CONTCAR POSCAR.CONTCAR
        INP_FILE=POSCAR.CONTCAR
        INPUTFMT=POSCAR
    fi
    if [[ $INP_FILE == "CONTCAR" ]];then
        cp CONTCAR POSCAR.CONTCAR
        INP_FILE=POSCAR.CONTCAR
        INPUTFMT=POSCAR
    fi
}

function xsd2xxf() {
    VCOM=`command -v vaspkit`
    if [ -n "$VCOM" ] && [ -n "$MCOM" ];then
        echo -e "106\n$1" | vaspkit > /dev/null
        if [ $? -eq 0 ];then
            gjfname=${1%.*}.gjf
            echo -e "100\n2\n10\n$gjfname\n0\nq" | Multiwfn POSCAR > /dev/null
        fi
    else
        awk '/XYZ=/{for(i=1;i<=NF;i++){if(index($i, "XYZ=")>0){gsub("\"|,|XYZ="," ",$i);print $i}}}' $1 > xsd2gjf_coord
        sed -i 's/^[[:blank:]]*//;s/[[:blank:]]*$//' xsd2gjf_coord
        awk '/XYZ=/{for(i=1;i<=NF;i++){if(index($i, "Components=")>0){gsub("\"|Components=|/>|>","",$i);print $i}}}' $1 > xsd2gjf_elem
        sed -i 's/^[[:blank:]]*//;s/[[:blank:]]*$//' xsd2gjf_elem
        awk '/AVector=/{for(i=1;i<=NF;i++){if(index($i, "AVector=")>0){gsub("\"|AVector=|,"," ",$i);print $i}}}' $1 > xsd2gjf_vect
        awk '/BVector=/{for(i=1;i<=NF;i++){if(index($i, "BVector=")>0){gsub("\"|BVector=|,"," ",$i);print $i}}}' $1 >> xsd2gjf_vect
        awk '/CVector=/{for(i=1;i<=NF;i++){if(index($i, "CVector=")>0){gsub("\"|CVector=|,"," ",$i);print $i}}}' $1 >> xsd2gjf_vect
        paste xsd2gjf_elem xsd2gjf_coord | awk '{printf "%-5s%16.8f%16.8f%16.8f\n",$1,$2,$3,$4}' > xsd2gjf_gjftail
        echo -e "# \n\nTitle\n\n0 1" > xsd2gjf_gjfhead
        num_lines=$(wc -l  xsd2gjf_vect | awk '{print $1}')
        if [[ $num_lines == 3 ]];then
            group_name=`awk '/GroupName=/{for(i=1;i<=NF;i++){if(index($i, "GroupName=")>0){gsub("\"|GroupName=","",$i);print $i}}}' $1`
            if [[ $group_name != P1 ]];then
                echo "Warning!!! the sysmetry of the xsd: $1 is not P1, the resulting structure may be wrong"
            fi
            awk '{printf "%-5s%16.8f%16.8f%16.8f\n","Tv",$1,$2,$3}' xsd2gjf_vect > xsd2gjf_temp
            mv xsd2gjf_temp xsd2gjf_vect
            cat xsd2gjf_gjfhead xsd2gjf_gjftail xsd2gjf_vect > ${1/.xsd/}.gjf
        else
            cat xsd2gjf_gjfhead xsd2gjf_gjftail > ${1/.xsd/}.gjf
        fi
        rm xsd2gjf_coord
        rm xsd2gjf_elem
        rm xsd2gjf_gjfhead
        rm xsd2gjf_gjftail
        rm xsd2gjf_vect
    fi
}


while [[ $# -ge 1 ]];do
    if [ ! -f $1 ];then
        echo "File $1 not exit"
        shift 1
        continue
    fi
    sed -i 's/\r$//' $1
    needhelp $1
    if [[ $ENGINE == "m" ]];then
        Multiwfn_fc $INP_FILE $OUTFMT 2> /dev/null
        if [[ $? != 0 ]] && [[ -n "$OCOM" ]];then
            obabel_fc $INP_FILE $OUTFMT
        fi
    fi 
    if [[ $ENGINE == "o" ]];then
        obabel_fc $INP_FILE $OUTFMT
        if [[ $? != 0 ]] && [[ -n "$MCOM" ]];then
            Multiwfn_fc $INP_FILE $OUTFMT
        fi
    fi 
    shift 1
done
